iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0

今日目標,即時顯示自己的手牌、別人的手牌數。

WebSocket

前面有提過手牌會透過 WebSocket 的單播來發給各個玩家,其他人的手牌數則透過對該房間的所有玩家群播來實現。
自己的手牌,我們讓 Client 訂閱 /my-hands;其他人的手牌數,則讓 Client 訂閱 /hands-info

  1. 修改 GameService,加入 getHandsByPlayerName() 用於取得指定玩家的手牌、sendHandsInfo() 用於發送各玩家手牌數,加入的片段程式碼為:
    public ArrayList<Card> getHandsByPlayerName(String name) {
        return gameStatus.getHandsByPlayerName(name);
    }
    
    public void sendHandsInfo(String roomId) {
        Player[] players = this.gameStatus.getPlayers(roomId);
        Map<String, Integer> handsNumber = new HashMap<>();
        for (Player player : players) {
            handsNumber.put(player.getName(), player.countHands());
        }
        this.roomService.sendMessageToRoom(roomId, "/queue/hands-info", handsNumber);
    }
    
  2. 在 game package 底下建立一個 java class,名稱為 GameWsController,加入方法 getMyHands() 取得自己的手牌、getHandsInfo() 取得其他人的手牌數,完整內容為:
    package com.example.game;
    
    import com.example.card.Card;
    import com.example.room.UserStatus;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.messaging.handler.annotation.MessageMapping;
    import org.springframework.messaging.simp.SimpMessagingTemplate;
    import org.springframework.stereotype.Controller;
    
    import java.security.Principal;
    import java.util.ArrayList;
    
    @Controller
    public class GameWsController {
        @Autowired
        private UserStatus userStatus;
    
        @Autowired
        private GameService gameService;
    
        @Autowired
        private SimpMessagingTemplate simpMessagingTemplate;
    
        @MessageMapping("/hands-info")
        public void getHandsInfo(Principal principal) {
            String name = principal.getName();
            String roomId = userStatus.getUserRoomId(name);
            this.gameService.sendHandsInfo(roomId);
        }
    
        @MessageMapping("/my-hands")
        public void getMyHands(Principal principal) {
            String name = principal.getName();
            ArrayList<Card> myHands = gameService.getHandsByPlayerName(name);
            simpMessagingTemplate.convertAndSendToUser(name, "/queue/my-hands", myHands);
        }
    }
    

頁面

再來,我們要藉由發送過來的訊息將手牌顯示出來,並根據其他人的手牌數顯示同等數量的牌。

  1. 修改 game.js,加入 WebSocket 連線方式以及相關方法 subscribeMyHands(),但我們先不撰寫函數怎麼運行,我們先單純把資料印出來,完整內容為:
    var websocket = new WebSocket();
    var myUsername = $("#my-username").val();
    
    $(document).ready(() => {
        websocket.connect('/connect', () => {
            subscribeMyHands();
            websocket.send("/my-hands", {});
        });
        relocatePlayedCards();
    })
    
    
    function subscribeMyHands() {
        websocket.subscribe("/user/queue/my-hands", (response) => {
            response = JSON.parse(response.body);
    
            console.log(response);  // 注意這邊的輸出
        });
    }
    
    function relocateMyHands() {
        let newLocationX = ($(".game-window").width() - $(".my-hands").width()) / 2;
        $(".my-hands").css("left", newLocationX);
    }
    
    function relocatePlayedCards() {
        let newLocationX = ($(".game-window").width() - $(".card-type").width()) / 2;
        $(".card-type").css("left", newLocationX);
    }
    
  2. 先只印出資料是因為要讓讀者們確認,我們傳過來的資料型態實際上是一個陣列,每個陣列是一個鍵值對(key-value pair),由 suit 和 number 組合而成,而且 suit 和 number 分別是我們一開始定義的 Enum 的名稱,所以我們要操作卡牌時,我們需要做一下轉換
  3. 加入轉換的對照表,並加入轉換方法,片段程式碼為:
    const SUITS = {
        SPADE : "♠️",
        HEART : "♥",
        DIAMOND : "♦",
        CLUB : "♣",
    };
    const NUMBERS = {
        ACE : "1",
        TWO : "2",
        THREE : "3",
        FOUR : "4",
        FIVE : "5",
        SIX : "6",
        SEVEN : "7",
        EIGHT : "8",
        NINE : "9",
        TEN : "10",
        JACK : "J",
        QUEEN : "Q",
        KING : "K",
    };
    
    function convertCard(card) {
        return `${SUITS[card.suit]}-${NUMBERS[card.number]}`;
    }
    
    • 轉換方法,我們簡單一點,直接把 suit 和 number 併成一個字串,並且用短線(dash, -) 隔開
  4. 加入轉換方法之後,我們就可以定義怎麼產生手牌了,修改後的完整內容為:
    var websocket = new WebSocket();
    var myUsername = $("#my-username").val();
    
    $(document).ready(() => {
        websocket.connect('/connect', () => {
            subscribeMyHands();
            websocket.send("/my-hands", {});
        });
        relocateMyHands();
        relocatePlayedCards();
    })
    
    const SUITS = {
        SPADE : "♠️",
        HEART : "♥",
        DIAMOND : "♦",
        CLUB : "♣",
    };
    const NUMBERS = {
        ACE : "1",
        TWO : "2",
        THREE : "3",
        FOUR : "4",
        FIVE : "5",
        SIX : "6",
        SEVEN : "7",
        EIGHT : "8",
        NINE : "9",
        TEN : "10",
        JACK : "J",
        QUEEN : "Q",
        KING : "K",
    };
    
    function subscribeMyHands() {
        websocket.subscribe("/user/queue/my-hands", (response) => {
            response = JSON.parse(response.body);
    
            let myHands = [];
            for (let card of response) {
                myHands.push(convertCard(card));
            }
            generateMyHands(myHands);
        });
    }
    
    
    function generateMyHands(myHands) {
        $(".my-hands").empty();
        let index = 0;
        for (let cards of myHands) {
            let tmp = cards.split("-");
            let color = (tmp[0] == SUITS.SPADE || tmp[0] == SUITS.CLUB) ? "#000000" : "#FF0000";
            $(".my-hands").append(`
                <div class="m-card text-center" style="color: ${color}" id="card-${tmp[0]}-${tmp[1]}" tabindex="${index++}">
                    <div>
                    ${tmp[0]}
                    <br>
                    ${tmp[1]}
                    </div>
                </div>
            `);
        }
        relocateMyHands();
    }
    
    
    function convertCard(card) {
        return `${SUITS[card.suit]}-${NUMBERS[card.number]}`;
    }
    
    function relocateMyHands() {
        let newLocationX = ($(".game-window").width() - $(".my-hands").width()) / 2;
        $(".my-hands").css("left", newLocationX);
    }
    
    function relocatePlayedCards() {
        let newLocationX = ($(".game-window").width() - $(".card-type").width()) / 2;
        $(".card-type").css("left", newLocationX);
    }
    
  5. 產生其他人的手牌,再加入 subscribeHandsInfo()generateOtherHands(),修改後的完整內容為:
    var websocket = new WebSocket();
    var myUsername = $("#my-username").val();
    var selectedCards = new Set();
    
    $(document).ready(() => {
        websocket.connect('/connect', () => {
            subscribeMyHands();
            subscribeHandsInfo();
            websocket.send("/my-hands", {});
            websocket.send("/hands-info", {});
        });
        relocatePlayedCards();
    })
    
    const SUITS = {
        SPADE : "♠️",
        HEART : "♥",
        DIAMOND : "♦",
        CLUB : "♣",
    };
    const NUMBERS = {
        ACE : "1",
        TWO : "2",
        THREE : "3",
        FOUR : "4",
        FIVE : "5",
        SIX : "6",
        SEVEN : "7",
        EIGHT : "8",
        NINE : "9",
        TEN : "10",
        JACK : "J",
        QUEEN : "Q",
        KING : "K",
    };
    
    function subscribeMyHands() {
        websocket.subscribe("/user/queue/my-hands", (response) => {
            response = JSON.parse(response.body);
    
            let myHands = [];
            for (let card of response) {
                myHands.push(convertCard(card));
            }
            generateMyHands(myHands);
        });
    }
    function subscribeHandsInfo() {
        websocket.subscribe("/user/queue/hands-info", (response) => {
            response = JSON.parse(response.body);
            let allUsers = Object.keys(response);
            let index = 0;
            let userIndex = 1;
            let count = 1;
            while (count < 4) {
                if (allUsers[index] === myUsername) {
                    userIndex = 2;
                }
                if (userIndex > 1) {
                    generateOtherHands(userIndex, response[allUsers[index]]);
                    userIndex++;
                    count++;
                }
                index = (index + 1) % 4;
            }
        })
    }
    
    function generateMyHands(myHands) {
        $(".my-hands").empty();
        let index = 0;
        for (let cards of myHands) {
            let tmp = cards.split("-");
            let color = (tmp[0] == SUITS.SPADE || tmp[0] == SUITS.CLUB) ? "#000000" : "#FF0000";
            $(".my-hands").append(`
                <div class="m-card text-center" style="color: ${color}" id="card-${tmp[0]}-${tmp[1]}" tabindex="${index++}">
                    <div>
                    ${tmp[0]}
                    <br>
                    ${tmp[1]}
                    </div>
                </div>
            `);
        }
        relocateMyHands();
    }
    function generateOtherHands(who, numbers) {
        $(`#user-${who}`).empty();
        for (let i=0; i<numbers; i++) {
            $(`#user-${who}`).append(`
            <div class="m-card text-center"></div>
            `);
        }
    }
    
    function convertCard(card) {
        return `${SUITS[card.suit]}-${NUMBERS[card.number]}`;
    }
    
    function relocateMyHands() {
        let newLocationX = ($(".game-window").width() - $(".my-hands").width()) / 2;
        $(".my-hands").css("left", newLocationX);
    }
    
    function relocatePlayedCards() {
        let newLocationX = ($(".game-window").width() - $(".card-type").width()) / 2;
        $(".card-type").css("left", newLocationX);
    }
    
  6. 發現手牌是亂的?那就排序吧~ 再次修改後的程式碼,加入 GENERAL_LEVELcompareByGeneral(),修改 subscribeMyHands()
    // 新增 - 取得他們之間的比較關係
    const GENERAL_LEVEL = {
        SPADE : 4,
        HEART : 3,
        DIAMOND : 2,
        CLUB : 1,
        ACE : 1,
        TWO : 2,
        THREE : 3,
        FOUR : 4,
        FIVE : 5,
        SIX : 6,
        SEVEN : 7,
        EIGHT : 8,
        NINE : 9,
        TEN : 10,
        JACK : 11,
        QUEEN : 12,
        KING : 13,
    };
    
    // 修改 - 多一個延遲執行,先讓使用者看一下之後再做排序
    function subscribeMyHands() {
        websocket.subscribe("/user/queue/my-hands", (response) => {
            response = JSON.parse(response.body);
    
            let myHands = [];
            for (let card of response) {
                myHands.push(convertCard(card));
            }
            generateMyHands(myHands);
    
            // 多了這邊~~
            setTimeout(() => {
                let sortedHands = response.sort(compareByGeneral);
                generateMyHands(sortedHands.map((card) => convertCard(card)));
            }, 1000);
        });
    }
    
    // 新增 - 比較兩個牌
    function compareByGeneral(a, b) {
        if (GENERAL_LEVEL[a.suit] > GENERAL_LEVEL[b.suit]) {
            return -1;
        }
        else if (GENERAL_LEVEL[a.suit] < GENERAL_LEVEL[b.suit]) {
            return 1;
        }
        else {
            return GENERAL_LEVEL[a.number]  - GENERAL_LEVEL[b.number];
        }
    }
    

Demo

老樣子,我們再去開 4 個帳號之後開始遊戲看看吧,應該會看到自己的牌產生,並重新排列了! /images/emoticon/emoticon07.gif


上一篇
Day 25 - Let's play a game
下一篇
Day 27 - 倒數計時
系列文
Spring Boot... 深不可測31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言